#include "OLView.h"
#include "OLApplication.h"
#include "OLModalBackground.h"
#include "OLMainWindow.h"

#include <QVariant>
#include <QPropertyAnimation>
#include <QDebug>

//@todo...
// - no anim support (set by client

const int ANIM_DURATION = 700;

OLView::OLView(QWidget *parent) :
    QWidget(parent), m_viewToRemove(0)
{
    connect(this, SIGNAL(handleResize(const QSize&)), OLApp, SIGNAL(viewSizeChanged(const QSize&)));

    m_anim = new QPropertyAnimation(this);
    m_anim->setPropertyName("pos");
    m_anim->setEasingCurve(QEasingCurve::InOutQuint);

    m_fader = new OLModalBackground(this);
    m_fader->setOpacity(0.0);
    m_fadeAnim = new QPropertyAnimation(m_fader, "opacity", this);

    connect(m_anim, SIGNAL(finished()), SLOT(handleCompleteAnimation()));
    connect(this, SIGNAL(viewAdded(QWidget*)), OLApp, SIGNAL(viewAdded(QWidget*)));
    connect(this, SIGNAL(baseViewActivated()), OLApp, SIGNAL(baseViewActivated()));
    connect(this, SIGNAL(viewRemoved(QWidget*)), OLApp, SIGNAL(viewRemoved(QWidget*)));
    connect(this, SIGNAL(viewFocusIn(QWidget*)), OLApp, SIGNAL(viewFocusIn(QWidget*)));
    connect(this, SIGNAL(viewFocusOut(QWidget*)), OLApp, SIGNAL(viewFocusOut(QWidget*)));
}

OLView::~OLView()
{
    delete m_viewToRemove;

    qDebug() << "Child Views:" << findChildren<QWidget*>("_OL_view").count();
}

void OLView::resizeEvent(QResizeEvent * event)
{
    QWidget::resizeEvent(event);

    m_fader->resize(size());

    emit handleResize(size());

    foreach(QWidget* view, m_viewStack) {
        view->resize(size());
    }
}

QWidget* OLView::currentView() const
{
    QWidget* currentView = 0;

    if( m_viewStack.count()>0 )
        currentView = m_viewStack[0];

    return currentView;
}

void OLView::setView(QWidget* view, OLApplication::OrientationOptions options, bool keep)
{
    view->resize(size());
    view->setObjectName("_OL_view");
    view->setProperty("_OL_keep", keep);
    view->setProperty("_OL_orientation", options);
    view->setParent(this);

    qDebug("setView");
    //killAnimation();
    QWidget* currentView = 0;
    int viewStackCount = m_viewStack.count();
    qDebug() << "stackCount:" << viewStackCount;
    switch(viewStackCount) {
        case 0: {
            // no item on the view stack
            m_viewStack.append(view);
            baseViewIn(view);
        } break;
        case 1: {
            // 1 item on the view stack must already be the base view
            currentView = m_viewStack[0];
            if( view!=currentView ) {
                QWidget* currentView = m_viewStack.takeFirst();
                emit viewFocusOut(currentView);
                keepOrDeleteView(currentView, true);
                m_viewStack.append(view);
                baseViewIn(view);
            }
        } break;
        default:
            // several items on the view stack, delete all the ones not visible
            currentView = m_viewStack.takeFirst();
            emit viewFocusOut(currentView);
            currentView->show();
            keepOrDeleteView(currentView, true);
            while(!m_viewStack.isEmpty()) {
                keepOrDeleteView(m_viewStack.takeFirst(), false);
            }
            m_viewStack.append(view);
            baseViewIn(view);
            emit baseViewActivated();
            break;
    }
}

void OLView::addStackedView(QWidget* view, OLApplication::OrientationOptions options, bool keep)
{
    qDebug("addStackedView");
    int stackCount = m_viewStack.count();
    if(stackCount>0) {
        QWidget* currentView = m_viewStack[0];
        if( currentView!=view ) {
            killAnimation();
            view->resize(size());
            view->setObjectName("_OL_view");
            view->setProperty("_OL_keep", keep);
            view->setProperty("_OL_orientation", options);
            view->setParent(this);
            m_viewStack.prepend(view);

            emit viewFocusOut(currentView);
            stackViewIn(view);
            emit viewAdded(view);
        }
    }
}

void OLView::back()
{
    qDebug("back");
    if(m_viewStack.count()>1) {
        killAnimation();
        QWidget* newView = m_viewStack[1];
        int animDuration = orientationChange(newView) ? 0 : ANIM_DURATION;
        newView->show();
        QWidget* currentView = m_viewStack.takeFirst();
        emit viewFocusOut(currentView);
        m_anim->setTargetObject(0);
        m_anim->setTargetObject(currentView);
        m_anim->setStartValue(currentView->pos());
        m_anim->setEndValue(QPoint(width(), 0));
        m_anim->setDuration(animDuration);

        m_fader->show();
        m_fadeAnim->setStartValue(1.0);
        m_fadeAnim->setEndValue(0.0);
        m_fadeAnim->setDuration(animDuration);

        currentView->raise();
        keepOrDeleteView(currentView, true);

        m_anim->start();
        m_fadeAnim->start();

        if(m_viewStack.count()==1) {
            emit baseViewActivated();
        }
    }

}


void OLView::baseViewIn(QWidget* view)
{
    killAnimation();

    int animDuration = orientationChange(view) ? 0 : ANIM_DURATION;
    view->show();
    m_anim->setTargetObject(0);
    m_anim->setTargetObject(view);
    m_anim->setStartValue(QPoint(-width(), 0));
    m_anim->setEndValue(QPoint(0, 0));
    m_anim->setDuration(animDuration);

    m_fader->show();
    m_fader->raise();
    m_fadeAnim->setStartValue(0.0);
    m_fadeAnim->setEndValue(1.0);
    m_fadeAnim->setDuration(animDuration);

    view->raise();

    m_anim->start();
    m_fadeAnim->start();
}

void OLView::stackViewIn(QWidget* view)
{
    int animDuration = orientationChange(view) ? 0 : ANIM_DURATION;
    view->show();
    m_anim->setTargetObject(0);
    m_anim->setTargetObject(view);
    m_anim->setStartValue(QPoint(width(), 0));
    m_anim->setEndValue(QPoint(0, 0));
    m_anim->setDuration(animDuration);

    m_fader->show();
    m_fader->raise();
    m_fadeAnim->setStartValue(0.0);
    m_fadeAnim->setEndValue(1.0);
    m_fadeAnim->setDuration(animDuration);

    view->raise();

    m_anim->start();
    m_fadeAnim->start();
}

void OLView::killAnimation()
{
    if(m_anim->state()==QAbstractAnimation::Running) {
        qDebug() << "Killing anim..." << (int)m_anim->targetObject();
        m_anim->stop();
        m_anim->targetObject()->setProperty("pos", m_anim->endValue());
        handleCompleteAnimation();
    }

    if(m_fadeAnim->state()==QAbstractAnimation::Running) {
        m_fadeAnim->stop();
    }

    qDebug() << "END: killAnimation";
}

void OLView::handleCompleteAnimation()
{
    qDebug() << "handleCompleteAnimation" << "ViewToRemove:" << (int)m_viewToRemove;

    // delete view waiting (if any)
    if(m_viewToRemove) {
        QVariant keepView = m_viewToRemove->property("_OL_keep");
        if( keepView.type()==QVariant::Bool && keepView.toBool() ) {
            m_viewToRemove->hide();
            emit viewRemoved(m_viewToRemove);
        } else {
            delete m_viewToRemove;
        }

        m_viewToRemove = 0;
        qDebug("ViewRemoved");
    }

    // hide the fader, no required for now
    m_fader->hide();

    // if there is more than one hide the last one
    int stackCount = m_viewStack.count();
    if(stackCount>1) {
        m_viewStack[1]->hide();
    }

    if(stackCount) {
        emit viewFocusIn(m_viewStack[0]);
    }

    qDebug() << "END: handleCompleteAnimation";
}

void OLView::keepOrDeleteView(QWidget* view, bool afterAnim)
{
    if(afterAnim) {
        qDebug() << "keepOrDel:" << (int)view;
        m_viewToRemove = view;
    } else {
        QVariant keepView = view->property("_OL_keep");
        if( keepView.type()==QVariant::Bool && keepView.toBool() ) {
            view->hide();
            emit viewRemoved(view);
        } else {
            delete view;
        }
    }

    qDebug() << "END: keepOrDeleteView";
}

bool OLView::orientationChange(QWidget* newView)
{
    bool requiresChange = true;
    OLMainWindow* mainWindow = qobject_cast<OLMainWindow*>(parentWidget());
    OLApplication::OrientationOptions currentOrientation = mainWindow->orientation();
    mainWindow->setOrientation(static_cast<OLApplication::OrientationOptions>(newView->property("_OL_orientation").toInt()));
    OLApplication::OrientationOptions newOrientation = mainWindow->orientation();

    if(currentOrientation==newOrientation)
        requiresChange = false;

    return requiresChange;
}

